#include "GLSLHelper.h"
#include <iostream>
#include <fstream>
#include <streambuf>
#include <cstdlib>

namespace GLSLHelper {

    std::string LoadSourceCode(std::string filename)
    {
        std::ifstream input(filename.c_str());
        if(!input)
        {
            std::cerr<<"Unable to open file "<<filename<<std::endl;
            exit(0);
        }

        std::string code((std::istreambuf_iterator<char>(input)), std::istreambuf_iterator<char>());

        return code;
    }

    GLuint PrepareShader(std::string shaderSource, GLenum shaderType)
    {
        std::string source = LoadSourceCode(shaderSource);
        char *sourceCode = new char[source.length() + 1];
        memset(sourceCode, 0, sizeof(char) * (source.length() + 1));
        strcpy(sourceCode, source.c_str());

        GLuint shader = glCreateShader(shaderType);
        if(!shader)
        {
            std::cerr<<"Unable to create shader with source "<<shaderSource<<std::endl;
            exit(0);
        }

        glShaderSource(shader, 1, (GLchar**)(&sourceCode), NULL);
        glCompileShader(shader);

        GLint flag;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &flag);
        if(!flag)
        {
            std::cerr<<"Errors occured in shader compiling stage"<<std::endl;

            GLsizei length = 0;
            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
            char *infoLog = new char[length];
            glGetShaderInfoLog(shader, length, &length, infoLog);

            std::cerr<<"ShaderInfoLog:"<<std::endl
                     <<infoLog<<std::endl;

            delete []infoLog;
            exit(0);
        }

        delete []sourceCode;

        return shader;
    }

    GLuint PrepareProgram(GLuint vtx_shader, GLuint frag_shader, GLuint geo_shader, GLuint tes_shader, GLuint comput_shader)
    {
        GLuint program = glCreateProgram();
        if(!program)
        {
            std::cerr<<"Unable to create program for shader "<<std::endl;
            exit(0);
        }

        if(vtx_shader) glAttachShader(program, vtx_shader);
        if(frag_shader) glAttachShader(program, frag_shader);
        if(geo_shader) glAttachShader(program, geo_shader);
        if(tes_shader) glAttachShader(program, tes_shader);
        if(comput_shader) glAttachShader(program, comput_shader);

        return program;
    }
	
	void LinkProgram(GLuint program)
	{
    	glLinkProgram(program);
    	glValidateProgram(program);

    	GLint flag;
    	glGetProgramiv(program, GL_LINK_STATUS, &flag);
    	if(!flag)
    	{
        	std::cerr<<"Errors occured in program linking stage"<<std::endl;

        	GLsizei length = 0;
        	glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
        	char *infoLog = new char[length];
        	glGetProgramInfoLog(program, length, &length, infoLog);

        	std::cerr<<"ProgramInfoLog:"<<std::endl
                 	<<infoLog<<std::endl;

        	delete []infoLog;
    	}

    	glGetProgramiv(program, GL_VALIDATE_STATUS, &flag);
    	if(!flag)
    	{
        	std::cerr<<"Program cannot pass validate stage"<<std::endl;

        	GLsizei length = 0;
        	glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
        	char *infoLog = new char[length];
        	glGetProgramInfoLog(program, length, &length, infoLog);

        	std::cerr<<"ProgramInfoLog:"<<std::endl
                 	<<infoLog<<std::endl;

        	delete []infoLog;

        	exit(0);
    	}
	}
}
